home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 096 / escreens.arc / ASMXMPLE.ASM next >
Assembly Source File  |  1985-08-08  |  17KB  |  737 lines

  1.  page 44,100
  2.  TITLE  ASMXMPLE
  3.  
  4. Video equ 10h          ;video functions interrupt number
  5. Keyboard equ 16h       ;keyboard functions interrupt number
  6. DOS equ 21h            ;call DOS interrupt number
  7. PrtSc equ 5h           ;Print Screen Bios interrupt
  8. CRTstatus equ 03DAh
  9. CRTmode equ 03D8h
  10. CursOff equ 16134
  11. portb equ 61h          ;I/O port B
  12.  
  13. STACK  Segment Para Stack 'Stack'
  14. Stak db 64 dup ("stack   ")
  15. STACK EndS
  16.  
  17. PARMSEG Segment Para 'data'
  18. Asciiz db 63 dup (?)   ;Device ID, path and filespec,
  19. Xasciiz db '$'         ; followed by at least one byte of
  20.                        ; of binary zero.
  21. Handle dw ?            ;Place to put 16 bit handle number
  22. Bufseg dw ?            ;Segment address of buffer
  23. Bufoff dw ?            ;Offset of buffer
  24. Bufsiz dw ?            ;Max bytes to read/write
  25. Actsiz dw ?            ;Actual bytes read
  26. Dskerr dw ?            ;DOS error code, if error occurs
  27. Parmseg EndS
  28.  
  29. DATASEG Segment Para 'data'
  30. IObuffer db 4096 dup (?)  ;I/O buffer for reading screens
  31.                           ;  from disk.
  32. Pgmstat db 0           ;program status switch
  33. Cover db 'ICOVER.SCN'
  34.       db 0
  35. FileSpec db 'INSTRC**.SCN'
  36.          db 0
  37. PageNbr db 0
  38. NewPage db '  '
  39. Ten db 10
  40. EdgeTab db 7,7,3,0,0,0,0,0
  41.         db 0,0,0,0,7,7,4,0
  42.         db 6,6,3,0,3,6,6,6
  43.         db 7,0,2,5,5,5,3,6
  44.         db 6,6,6,3,6,6,6,6
  45.         db 3,3,11,15,10,0,0,0
  46. StartOff dw 0
  47. Keycodes dw 0
  48. Answer db 256 dup (?)
  49. MaxChars dw 0
  50. Attributes dw 0
  51. Buzzit db 0
  52. Dataseg EndS
  53.  
  54. ScreenSeg Segment at 0B800h ;location of color screen mem
  55. Page0 db 4096 dup (?)
  56. Page1 db 4096 dup (?)
  57. Page2 db 4096 dup (?)
  58. Page3 db 4096 dup (?)
  59. ScreenSeg EndS
  60.  
  61. CODE Segment Para 'code'
  62.  assume cs:CODE
  63.  
  64. MAIN Proc Far
  65.  
  66.  push ds
  67.  mov ax,0
  68.  push ax
  69.  assume ds:dataseg
  70.  mov ax,dataseg
  71.  mov ds,ax
  72.  mov dl,0              ;set starting page number to zero
  73.  mov PageNbr,dl
  74.  call ShowCover        ;Put instructions cover on screen
  75.  cmp Pgmstat,0
  76.  jne InstructX         ;some was wrong
  77. PageMore:
  78.  call PutScreen        ;Get instruction screen from disk
  79.                        ; and display it.
  80.  call NextNumber       ;get next page number, etc.
  81.  cmp PageNbr,255
  82.  jne PageMore
  83. InstructX:
  84.  mov ah,11             ;set color palette
  85.  mov bh,0              ;set boarder color
  86.  mov bl,0              ;black
  87.  int video             ;call bios video routine
  88.  mov ah,0              ;set mode function
  89.  mov al,3              ;80 x 25 color
  90.  int video             ;bios call to video rom routines
  91.  mov ah,5              ;select active display page
  92.  mov al,0              ;page 0
  93.  int video             ;bios call
  94.  mov ah,1
  95.  mov ch,7
  96.  mov cl,7
  97.  ret                   ;return to dos
  98. MAIN EndP
  99.  
  100. ShowCover Proc Near
  101.  push ds
  102.  push es
  103.  call Cover1
  104.  assume ds:dataseg
  105.  cmp PageNbr,0
  106.  jne CoverX
  107. AnyKey:
  108.  mov ah,0              ;read next key struck from keyboard
  109.  int keyboard          ;call bios keyboard routine
  110. CoverX:
  111.  pop es
  112.  pop ds
  113.  ret
  114. ShowCover EndP
  115.  
  116. Cover1 Proc Near
  117.  push ds
  118.  push es
  119.  assume ds:dataseg
  120.  assume es:parmseg
  121.  mov ax,parmseg
  122.  mov es,ax
  123.  cmp PageNbr,0
  124.  jne CoverOk
  125. ;Set disk parms, start by moving file spec to Asciiz.
  126.  mov si,offset cover
  127.  mov di,offset asciiz
  128.  mov cx,64
  129.  cld
  130.  rep movsb
  131.  mov es:bufseg,ds      ;set segment address of buffer
  132.  mov ax,offset IObuffer ;set offset of buffer
  133.  mov es:bufoff,ax
  134.  mov ax,4008           ;set size of screen block
  135.  mov es:bufsiz,ax
  136.  call GetFile          ;get screen from disk
  137.  cmp es:DskErr,0
  138.  je CoverOK            ;read was OK
  139.  mov al,1
  140.  mov pgmstat,al
  141.  jmp CoverX
  142. Coverok:
  143.  mov al,3              ;80 x 25 color
  144.  mov ah,0              ;set mode
  145.  int video             ;call bios video routine
  146.  mov ah,5              ;select active display page
  147.  mov al,3              ;active page 3
  148.  int video             ;call bios video routine
  149.  mov ah,11             ;set color palette
  150.  mov bh,0              ;set boarder color
  151.  mov bl,1              ;dark blue
  152.  int video             ;call bios video routine
  153.  mov ah,1              ;make cursor invisible
  154.  mov ch,31
  155.  mov cl,31
  156.  int video
  157.  cmp PageNbr,0
  158.  jne Cover1X
  159.  mov dx,CRTmode        ;turn off the display
  160.  mov al,29h            ;mode byte for 80 column color
  161.  and al,11110111b
  162.  out dx,al
  163.  assume es:screenseg
  164.  mov ax,screenseg
  165.  mov es,ax
  166.  mov si,offset IObuffer+7 ;Move screen from I/O buffer to
  167.  mov di,offset Page3      ; to actual screen memory.
  168.  cld
  169.  mov cx,2000
  170.  rep movsw
  171.  mov dx,CRTmode        ;turn display back on
  172.  mov al,29h            ;80 x 25 color mode byte
  173.  out dx,al
  174.  call beep
  175. Cover1X:
  176.  pop es
  177.  pop ds
  178.  ret
  179. Cover1 EndP
  180.  
  181. PutScreen Proc Near
  182.  push ds
  183.  push es
  184. ReStart:
  185.  assume ds:dataseg
  186.  mov al,PageNbr        ;get instruction screen page number
  187.  call ConvByte         ;convert to decimal display
  188.  mov FileSpec+6,dh     ;and put it into the file spec
  189.  mov FileSpec+7,dl
  190.  assume es:parmseg
  191.  mov ax,parmseg
  192.  mov es,ax
  193. ;Set disk parms, start by moving file spec to Asciiz.
  194.  mov si,offset FileSpec
  195.  mov di,offset asciiz
  196.  mov cx,64
  197.  cld
  198.  rep movsb
  199.  mov es:bufseg,ds      ;set segment address of buffer
  200.  mov ax,offset IObuffer ;set offset of buffer
  201.  mov es:bufoff,ax
  202.  mov ax,4008           ;set size of screen block
  203.  mov es:bufsiz,ax
  204.  call GetFile          ;get screen from disk
  205.  cmp es:DskErr,0
  206.  je ScreenOK           ;read was OK
  207.  jmp NoPage            ;not a valid screen page number
  208. ScreenOK:
  209.  call SetEdge
  210.  mov dx,CRTmode        ;turn off the display
  211.  mov al,29h            ;mode byte for 80 column color
  212.  and al,11110111b
  213.  out dx,al
  214.  assume es:screenseg
  215.  mov ax,screenseg
  216.  mov es,ax
  217.  mov si,offset IObuffer+7 ;Move screen from I/O buffer to
  218.  mov di,offset Page3    ; to actual screen memory.
  219.  cld
  220.  mov cx,2000
  221.  rep movsw
  222.  mov dx,CRTmode        ;turn display back on
  223.  mov al,29h            ;80 x 25 color mode byte
  224.  out dx,al
  225.  call beep
  226.  jmp PSexit
  227. NoPage:
  228.  call buzz
  229.  call ClearAns
  230.  mov al,0
  231.  mov PageNbr,al
  232.  jmp ReStart
  233. PSexit:
  234.  pop es
  235.  pop ds
  236.  ret
  237. PutScreen EndP
  238.  
  239. NextNumber Proc Near
  240.  push ds
  241.  push es
  242. WaitInput:
  243.  mov si,CursOff        ;offset to cursor position on screen
  244.  mov cx,2              ;max characters in answer
  245.  mov dh,0BCh           ;cursor color atribute
  246.  mov dl,031h           ;answer color atribute
  247.  call Xanswer
  248.  assume es:dataseg
  249.  mov ax,dataseg
  250.  mov es,ax
  251.  cmp dh,1
  252.  je PrevPage           ;go to previous page
  253.  cmp dh,2
  254.  je NextPage           ;go to next page
  255.  cmp cx,0
  256.  je PageZero           ;go to page 0
  257.  cmp cx,01FFh
  258.  je MainMenu           ;escape back to main menu
  259.  push cx
  260.  mov di,offset NewPage ;move answer to local work area
  261.  cld
  262.  mov cx,2
  263.  rep movsb
  264.  pop cx
  265.  cmp cx,1
  266.  jne TwoDigit
  267. OneDigit:
  268.  call ConvOne
  269.  cmp ah,0
  270.  jne BadNumber
  271.  mov es:PageNbr,al
  272.  jmp NNexit
  273. TwoDigit:
  274.  call ConvTwo
  275.  cmp ah,0
  276.  jne BadNumber
  277.  mov es:PageNbr,al
  278.  jmp NNexit
  279. PrevPage:
  280.  cmp es:PageNbr,0
  281.  je BadNumber
  282.  dec es:PageNbr
  283.  jmp NNexit
  284. Nextpage:
  285.  inc es:PageNbr
  286.  jmp NNexit
  287. PageZero:
  288.  mov al,0
  289.  mov es:PageNbr,al
  290.  jmp NNexit
  291. MainMenu:
  292.  mov al,255
  293.  mov es:PageNbr,al
  294.  jmp NNexit
  295. BadNumber:
  296.  call buzz
  297.  call ClearAns
  298.  jmp WaitInput
  299. NNexit:
  300.  pop es
  301.  pop ds
  302.  ret
  303. NextNumber EndP
  304.  
  305. ConvOne Proc Near
  306.  push ds
  307.  push es
  308.  assume ds:dataseg
  309.  mov ax,dataseg
  310.  mov ds,ax
  311.  mov al,NewPage
  312.  cmp al,30h
  313.  jl NotNumber1
  314.  cmp al,39h
  315.  jg NotNumber1
  316.  sub al,30h            ;convert to binary
  317.  mov ah,0
  318.  jmp COexit
  319. NotNumber1:
  320.  mov ah,1
  321. COexit:
  322.  pop es
  323.  pop ds
  324.  ret
  325. ConvOne EndP
  326.  
  327. ConvTwo Proc Near
  328.  push ds
  329.  push es
  330.  assume ds:dataseg
  331.  mov ax,dataseg
  332.  mov ds,ax
  333.  mov al,NewPage
  334.  cmp al,30h
  335.  jl NotNumber2
  336.  cmp al,39h
  337.  jg NotNumber2
  338.  sub al,30h            ;convert to binary
  339.  mul Ten               ;multiply by 10
  340.  mov dl,al             ;save in dl
  341.  mov al,NewPage+1
  342.  cmp al,30h
  343.  jl NotNumber2
  344.  cmp al,39h
  345.  jg NotNumber2
  346.  sub al,30h            ;convert to binary
  347.  add al,dl             ;add 10's value
  348.  mov ah,0
  349.  jmp CTexit
  350. NotNumber2:
  351.  mov ah,1
  352. CTexit:
  353.  pop es
  354.  pop ds
  355.  ret
  356. ConvTwo EndP
  357.  
  358. ClearAns Proc Near
  359.  push ds
  360.  push es
  361.  assume ds:screenseg
  362.  mov ax,screenseg
  363.  mov ds,ax
  364.  mov al,20h
  365.  mov bx,CursOff
  366.  mov [page0 + bx],al
  367.  inc bx
  368.  mov [page0 + bx],al
  369.  inc bx
  370.  mov [page0 + bx],al
  371.  pop es
  372.  pop ds
  373.  ret
  374. ClearAns EndP
  375.  
  376. SetEdge Proc Near
  377.  push ds
  378.  push es
  379.  assume ds:dataseg
  380.  mov ax,dataseg
  381.  mov ds,ax
  382.  mov bl,PageNbr
  383.  mov bh,0
  384.  mov ch,[EdgeTab + bx]
  385.  mov ah,11
  386.  mov bh,0
  387.  mov bl,ch
  388.  int video
  389.  pop es
  390.  pop ds
  391.  ret
  392. SetEdge EndP
  393.  
  394. ;Subroutine to get a binary block of data from disk.
  395. ;It opens file, reads entire file (as one block of data)
  396. ;into memory, closes file.
  397. ;
  398. ;Enter with:
  399. ;  - Device, path, filespec and binary zero in Asciiz.
  400. ;  - Segment address of buffer in Bufseg.
  401. ;  - Offset of buffer in Bufoff.
  402. ;  - Max number of bytes to read, in Bufsiz.
  403. ;
  404. ;On exit:
  405. ;  - Handle will contain handle number.
  406. ;  - Actsiz will contain actual number of bytes read, or
  407. ;    zero if file was not found on disk.
  408. ;  - Dskerr will contain the DOS error code, or zero if
  409. ;    no error.
  410.  
  411. Getfile Proc Near
  412.  push ds               ;save registers of calling routine
  413.  push es
  414.  push si
  415.  push di
  416.  push ax
  417.  push bx
  418.  push cx
  419.  push dx
  420.  assume ds:parmseg
  421.  mov ax,parmseg        ;set DS to PARMSEG segment
  422.  mov ds,ax             ; -which sets DS to Asciiz
  423.  mov dx,offset asciiz  ; -set offset to Asciiz
  424.  mov al,0              ;set AL to "open for reading"
  425.  mov ah,3dh            ;set AH to "open a file" function
  426.  int DOS               ;call DOS
  427.  jc gfopenerr          ;error on open
  428.  mov handle,ax         ;save handle
  429.  mov bx,handle         ;load BX with file handle
  430.  mov cx,bufsiz         ;load CX with # of bytes to read
  431.  mov dx,bufoff         ;load DX with offset of buffer
  432.  mov ax,bufseg         ;load DS with segment address
  433.  mov ds,ax             ;  of buffer
  434.  mov ah,3fh            ;"read from a file" function
  435.  int DOS               ;call DOS
  436.  push ax               ;save # of bytes read on stack
  437.  mov ax,parmseg        ;restore DS to diskparms segment
  438.  mov ds,ax             ;
  439.  pop ax                ;get # of bytes read
  440.  jc gfreaderr          ;error on read
  441.  mov actsiz,ax         ;save # of bytes read, in diskparms
  442.  mov bx,handle         ;load BX with file handle
  443.  mov ah,3eh            ;"close a file handle" function
  444.  int DOS               ;call DOS
  445.  jc gfcloserr          ;error on close
  446.  mov dskerr,0          ;set error code to zero (no error)
  447. Gfback:
  448.  pop dx                ;restore registers of calling
  449.  pop cx                ; routine.
  450.  pop bx
  451.  pop ax
  452.  pop di
  453.  pop si
  454.  pop es
  455.  pop ds
  456.  ret                   ;return to calling routine
  457. ;ERROR ON OPEN: Possible error returns are:
  458. ;  2 - File not found
  459. ;  4 - To many open files (no handles left)
  460. ;  5 - Access denied
  461. ; 12 - Invalid access code
  462. Gfopenerr:
  463.  mov ah,1              ;set AH to 1 for open error,
  464.                        ;AL already contains error code
  465.  mov dskerr,ax         ;put error code in diskparms
  466.  jmp gfback
  467. ;ERROR ON READ: Possible error returns are:
  468. ;  5 - Access denied
  469. ;  6 - Invalid handle
  470. Gfreaderr:
  471.  mov ah,2              ;set AH to 2 for read error
  472.  mov dskerr,ax         ;put error code in diskparms
  473.  jmp gfback
  474. ;ERROR ON CLOSE: Possible error return is:
  475. ;  6 - Invalid handle
  476. Gfcloserr:
  477.  mov ah,3              ;set AH to 3 for close error
  478.  mov dskerr,ax         ;put error code in diskparms
  479.  jmp gfback
  480. Getfile EndP
  481.  
  482. Beep Proc Near
  483.  push ax               ;save registers of calling program
  484.  push dx
  485.  push cx
  486.  push bx
  487.  mov ax,0
  488.  mov dx,12h
  489.  mov cx,7d0h
  490.  div cx
  491.  mov bx,ax
  492.  mov al,10110110b
  493.  out 43h,al
  494.  mov ax,bx
  495.  out 42h,al
  496.  mov al,ah
  497.  out 42h,al
  498.  in al,portb
  499.  or al,3
  500.  out portb,al
  501.  mov cx,07fffh         ;set up counter for duration of beep
  502. wait:
  503.  loop wait
  504.  in al,portb
  505.  and al,11111100b
  506.  out portb,al
  507.  pop bx                ;restore registers of calling pgm
  508.  pop cx
  509.  pop dx
  510.  pop ax
  511.  ret
  512. BEEP EndP
  513.  
  514. BUZZ Proc Near
  515.  push ax               ;save registers of calling program
  516.  mov ax,ds
  517.  push ax
  518.  push bx
  519.  push cx
  520.  push dx
  521.  assume ds:dataseg
  522.  mov ax,dataseg        ;set DS for this routine
  523.  mov ds,ax
  524.  mov buzzit,16         ;set buzz counter
  525. Buzzone:
  526.  mov ax,0
  527.  mov dx,12h
  528.  mov cx,900
  529.  div cx
  530.  mov bx,ax
  531.  mov al,10110110b
  532.  out 43h,al
  533.  mov ax,bx
  534.  out 42h,al
  535.  mov al,ah
  536.  out 42h,al
  537.  in al,portb
  538.  or al,3
  539.  out portb,al
  540.  mov cx,0affh
  541. Buzz1:
  542.  loop buzz1
  543.  in al,portb
  544.  and al,11111100b
  545.  out portb,al
  546.  mov cx,7ffh
  547. Buzz2:
  548.  loop buzz2
  549.  dec buzzit
  550.  jnz buzzone
  551.  pop dx
  552.  pop cx
  553.  pop bx
  554.  pop ax
  555.  mov ds,ax
  556.  pop ax
  557.  ret
  558. BUZZ EndP
  559.  
  560. ;Enter with binary value in AL.
  561. ;On return, the result is in: (assume result is 234)
  562. ;     AH = 2, DH = 3, DL = 4
  563. Convbyte Proc Near
  564.  push ds
  565.  push es
  566.  mov ah,48
  567.  mov dh,48
  568.  mov dl,48
  569. Test1:
  570.  cmp al,100
  571.  jl test2
  572.  sub al,100
  573.  inc ah
  574.  jmp test1
  575. Test2:
  576.  cmp al,10
  577.  jl test3
  578.  sub al,10
  579.  inc dh
  580.  jmp test2
  581. Test3:
  582.  add dl,al
  583.  pop es
  584.  pop ds
  585.  ret
  586. Convbyte EndP
  587.  
  588. ;Enter with:
  589. ;  SI = offset of cursor position in screen memory
  590. ;  CX = maximum number of characters in answer
  591. ;  DH = color atribute of cursor
  592. ;  DL = color atribute of answer
  593. ;On return:
  594. ;  The answer is on the screen, followed by the cursor.
  595. ;  The answer is also in a string pointed to by DS SI,
  596. ;  with CX set to the number of characters in the answer.
  597. ;  If Escape was pushed CX will = 01FFh.
  598. ;  DH will = 0, unless PgUp pushed then DH = 1
  599. ;                   or PgDn pushed then DH = 2.
  600. Xanswer Proc Near
  601.  push ds
  602.  push es
  603.  assume ds:screenseg
  604.  mov ax,screenseg
  605.  mov ds,ax
  606.  assume es:dataseg
  607.  mov ax,dataseg
  608.  mov es,ax
  609.  mov es:Attributes,dx  ;save color attributes
  610.  mov es:StartOff,si    ;save beginning offset
  611.  mov es:MaxChars,cx    ;save maximum number of characters
  612.                        ; in answer.
  613.  mov cx,256
  614.  mov bx,0
  615.  mov al,20h
  616. ClearAnswer:
  617.  mov [es:answer + bx],al
  618.  inc bx
  619.  loop ClearAnswer
  620.  mov cx,0              ;initialize character counter
  621. NextChar:
  622.  call PutCursor        ;Put cursor on screen
  623.  mov ah,0
  624.  int keyboard          ;read next keyboard entry
  625.  mov es:keycodes,ax    ;save codes
  626.  cmp al,20h
  627.  jl ChekEdit           ;not a printable character
  628.  cmp al,7Eh
  629.  jg ChekEdit           ;not a printable character
  630.  cmp cx,es:MaxChars
  631.  jge BadChar           ;already have max number of char's
  632.  call PutChar          ;put character on screen
  633.  inc si                ;bump offset to screen by 1 char'
  634.  inc si                ; which is 2 bytes.
  635.  mov bx,cx             ;move counter to pointer
  636.  mov ax,es:keycodes    ;get character code
  637.  mov [es:Answer + bx],al ;and put in answer string
  638.  inc cx                ;bump character counter
  639.  jmp NextChar
  640. ChekEdit:
  641.  cmp ax,0E08h          ;backspace
  642.  je BackUp
  643.  cmp ax,4B00h          ;left arrow
  644.  je BackUp
  645.  cmp ax,1C0Dh          ;return
  646.  je Return
  647.  cmp ax,011Bh          ;escape
  648.  je Escape
  649.  cmp ah,49h            ;PgUp
  650.  je PageUp
  651.  cmp ah,51h            ;PgDn
  652.  je PageDown
  653. BadChar:
  654.  call buzz             ;invalid character or control
  655.  jmp NextChar
  656. BackUp:
  657.  cmp cx,0
  658.  jle BadChar           ;can't back up
  659.  mov es:keycodes,02020h ;set keycodes to space
  660.  call PutChar          ;blank out cursor on screen
  661.  dec cx                ;decrement character counter
  662.  dec si                ;decrement offset pointer
  663.  dec si                ; (2 bytes per character)
  664.  jmp NextChar          ;and start over
  665. Escape:
  666.  mov cx,01FFh
  667.  mov dh,0
  668.  jmp Return2
  669. PageUp:
  670.  mov dh,1
  671.  jmp Return2
  672. PageDown:
  673.  mov dh,2
  674.  jmp Return2
  675. Return:
  676.  mov dh,0
  677. Return2:
  678.  mov es:keycodes,02020h   ;set keycodes to space
  679.  call PutChar          ;blank out cursor on screen
  680.  assume ds:dataseg
  681.  mov ax,dataseg
  682.  mov ds,ax
  683.  mov si,offset answer
  684.  pop es
  685.  pop ds
  686.  ret
  687. Xanswer EndP
  688.  
  689. PutCursor Proc Near
  690.  assume ds:screenseg
  691.  assume es:dataseg
  692.  mov bx,es:attributes
  693.  mov bl,219
  694.  mov dx,CRTstatus
  695. Wait1:
  696.  in al,dx
  697.  test al,1
  698.  jnz Wait1
  699. Wait2:
  700.  in al,dx
  701.  test al,1
  702.  jz Wait2
  703.  cli
  704.  mov [Page0 + si],bl
  705.  mov [Page0 + 1  + si],bh
  706.  sti
  707.  ret
  708. PutCursor EndP
  709.  
  710. PutChar Proc Near
  711.  push dx
  712.  assume ds:screenseg
  713.  assume es:dataseg
  714.  mov bx,es:attributes
  715.  mov bh,bl
  716.  mov ax,es:keycodes
  717.  mov bl,al
  718.  mov dx,CRTstatus
  719. WaitA:
  720.  in al,dx
  721.  test al,1
  722.  jnz WaitA
  723. WaitB:
  724.  in al,dx
  725.  test al,1
  726.  jz WaitB
  727.  cli
  728.  mov [Page0 + si],bl
  729.  mov [Page0 + 1  + si],bh
  730.  sti
  731.  pop dx
  732.  ret
  733. PutChar EndP
  734.  
  735. CODE EndS
  736. END
  737.